/*
 *  Filename:lsv_gc_check_queue.c
 *  Description:
 *
 *  Created on: 2017年3月27日
 *  Author: Asdf(825674301)
 */

#include <time.h>

#include "volume_proto.h"

#include "lsv_conf.h"
#include "lsv_help.h"
#include "lsv_log.h"
#include "lsv_volume.h"

void _lsv_gc_check_queue_push(lsv_gc_check_queue_t *check_queue, lsv_gc_logctrl_t* logctrl) {
        CHUNK_HISTORY("add to check queue head", logctrl);

        list_add(&logctrl->hook, &check_queue->queue);
        check_queue->count++;
}

// FIFO add tail, del from head
void _lsv_gc_check_queue_offer(lsv_gc_check_queue_t *check_queue, lsv_gc_logctrl_t* logctrl) {
        CHUNK_HISTORY("add to check queue", logctrl);

        list_add_tail(&logctrl->hook, &check_queue->queue);
        check_queue->count++;
}

void _lsv_gc_check_queue_poll(lsv_gc_check_queue_t *check_queue, lsv_gc_logctrl_t** logctrl) {
        *logctrl = (lsv_gc_logctrl_t *) check_queue->queue.next;
        list_del_init(check_queue->queue.next);
        check_queue->count--;

        CHUNK_HISTORY("poll from check queue", (*logctrl));
}

/**
 *
 * @param lsv_info
 * @param snap_id
 * @param lba
 * @param chunk_id
 * @param chunk_page_off
 * @return 1 引用
 * @return 0 无引用
 */
static inline int __lsv_gc_lba_check(lsv_volume_proto_t *lsv_info, uint32_t snap_id,
                                         lsv_u64_t lba, lsv_u32_t chunk_id, lsv_u32_t chunk_page_off) {
        //0可以，1不可以，负值出错
        return lsv_bitmap_lba_check(lsv_info, lba, chunk_id, chunk_page_off, snap_id);
}

int lsv_gc_check(lsv_volume_proto_t *lsv_info, lsv_gc_logctrl_t *logctrl) {
        int ret = 0;
        lsv_u32_t i;
        lsv_log_head_t *log_head;
        lsv_log_hlog_t *log_hlog;

        ((lsv_log_info_t*) lsv_info->log_info)->gc_context->stat_info.task_check_count++;

        void *log = NULL;

#if LSV_GC_LOGCTRL_HLOG_TYPE==2
        if (NULL == logctrl->hlog) {
                // @malloc ERR1
                log = malloc(LSV_PAGE_SIZE);
                if (NULL == log) {
                        ret = -ENOMEM;
                        DERROR("malloc log failed, ret %d\n", ret);
                        goto ERR0;
                }

                ret = lsv_volume_chunk_read_data(lsv_info, LSV_THIS_VOL_INO, logctrl->chunk_id, 0, LSV_PAGE_SIZE, log);
                if (ret < 0) {
                        DERROR("read chunk_id %u ret %d\n", logctrl->chunk_id, ret);
                        goto ERR1;
                }

                log_head = (lsv_log_head_t *) (log + LSV_LOG_HEAD);

                uint32_t crc1 = log_head->crc;

                log_head->crc = 0;
                uint32_t crc2 = page_crc32(log);
                log_head->crc = crc1;

                DINFO("read hlog, chunk_id %u head crc %u\n", logctrl->chunk_id, crc1);
                YASSERT(crc1 == crc2);

                // update
                logctrl->head = *log_head;
                logctrl->hlog = log + LSV_LOG_HLOG;
        }
#endif

        log_head = &logctrl->head;
        log_hlog = logctrl->hlog;

        // TODO core
        YASSERT(log_head->page_count > 0 && log_head->page_count <= LSV_LOG_PAGE_NUM);

        CHUNK_HISTORY("gc check", logctrl);

        int is_ref = 0;
        int count = 0;

        // YASSERT(logctrl->lp_bm.use_count == 0);

        for (i = 0; i < log_head->page_count; i++) {
                if (!lp_bitmap_get(&logctrl->lp_bm, i)) {
                        is_ref = __lsv_gc_lba_check(lsv_info,
                                                    log_hlog[i].snap_id,
                                                    log_hlog[i].lba,
                                                    logctrl->chunk_id,
                                                    lsv_log_slogpos2chkoffset(i));
                        if (!is_ref) {
                                // 可以回收
                                lp_bitmap_set_inactive(&logctrl->lp_bm, i);
                                count++;
                        } else {
                                DINFO("refmap read snap_id %u lba %ju chunk_id %u page %u/%u is_ref %u\n",
                                      log_hlog[i].snap_id,
                                      log_hlog[i].lba,
                                      logctrl->chunk_id,
                                      i,
                                      log_head->page_count,
                                      is_ref);
                        }
                }
        }

        CHUNK_HISTORY("gc check end", logctrl);
        // YASSERT(logctrl->lp_bm.use_count == count);

        logctrl->gc_value = logctrl->lp_bm.inactive_page_count;

        ((lsv_log_info_t*) lsv_info->log_info)->gc_context->stat_info.task_check_count--;
        // DINFO_LOGGC_STATISTICS(&((lsv_log_info_t* )lsv_info->log_info)->gc_context->stat_info);

        if (log) {
                free(log);
                logctrl->hlog = NULL;
        }
        return 0;
ERR1:
        if (log) {
                free(log);
                logctrl->hlog = NULL;
        }
ERR0:
        return ret;
}

int _lsv_gc_check_queue_size(lsv_volume_proto_t *lsv_info) {
        lsv_u32_t chunk_num;
        chunk_num = lsv_info->volume_proto->table1.fileinfo.logical_size / (LSV_LOG_PAGE_NUM * LSV_PAGE_SIZE) + 1;
        chunk_num = chunk_num / (1 - LSV_GC_DIRTY_RATIO);

        return _min(chunk_num, LSV_GC_CHECK_QUEUE_SIZE);
}
